#ifndef __PVMMIXDATAFASTINT_H__
#define __PVMMIXDATAFASTINT_H__

#include "pvm/pvmdefs.h"

PVM_FORCEINLINE int mix8mono(signed char** bufSrc, int nChanels)
{
	int val = 0;
	while(nChanels>0)
	{
		val += **bufSrc;
		(*bufSrc)++;
		nChanels--;
	}
	return val;
}

PVM_FORCEINLINE int mix16mono(short** bufSrc, int nChanels)
{
	int val = 0;
	while(nChanels>0)
	{
		val += **bufSrc;
		(*bufSrc)++;
		nChanels--;
	}
	return val;
}

PVM_FORCEINLINE void mix8stereo(signed char** bufSrc, int nChanels, int &l, int &r)
{
	l = r = 0;
	int n = nChanels&(~1);
	while(n>0)
	{
		if((n&1)==0)
			l += **bufSrc;
		else
			r += **bufSrc;
		(*bufSrc)++;
		n--;
	}

	if(nChanels&1)
	{
		l += **bufSrc;
		r += **bufSrc;
		(*bufSrc)++;
	}

	return;
}

PVM_FORCEINLINE void mix16stereo(short** bufSrc, int nChanels, int &l, int &r)
{
	l = r = 0;
	int n = nChanels&(~1);
	while(n>0)
	{
		if((n&1)==0)
			l += **bufSrc;
		else
			r += **bufSrc;
		(*bufSrc)++;
		n--;
	}

	if(nChanels&1)
	{
		l += **bufSrc;
		r += **bufSrc;
		(*bufSrc)++;
	}

	return;
}

PVM_FORCEINLINE int MixDataToMono8(signed char* srcBuffer, int srcChanels, int srcSamples, signed char* dstBuffer, int mixingVolume)
{
	int dstOffset = 0;
	int mixed;

	if(mixingVolume==256)
	{
		for(int i=0; i<srcSamples; i+=srcChanels, dstOffset++)
		{
			mixed = dstBuffer[dstOffset] + mix8mono(&srcBuffer, srcChanels);
			if(mixed>127)
				mixed = 127;
			else if(mixed<-128)
				mixed = -128;
			dstBuffer[dstOffset] = mixed;
		}
	}
	else
	{
		for(int i=0; i<srcSamples; i+=srcChanels, dstOffset++)
		{
			mixed = dstBuffer[dstOffset] + ((mix8mono(&srcBuffer, srcChanels)*mixingVolume)>>8);
			if(mixed>127)
				mixed = 127;
			else if(mixed<-128)
				mixed = -128;
			dstBuffer[dstOffset] = mixed;
		}
	}

	return dstOffset;
}

PVM_FORCEINLINE int MixDataToMono16(short* srcBuffer, int srcChanels, int srcSamples, short* dstBuffer, int mixingVolume)
{
	int dstOffset = 0;
	int mixed;

	if(mixingVolume==256)
	{
		for(int i=0; i<srcSamples; i+=srcChanels, dstOffset++)
		{
			mixed = dstBuffer[dstOffset] + mix16mono(&srcBuffer, srcChanels);
			if(mixed>32767)
				mixed = 32767;
			else if(mixed<-32768)
				mixed = -32768;
			dstBuffer[dstOffset] = mixed;
		}
	}
	else
	{
		for(int i=0; i<srcSamples; i+=srcChanels, dstOffset++)
		{
			mixed = dstBuffer[dstOffset] + ((mix16mono(&srcBuffer, srcChanels)*mixingVolume)>>8);
			if(mixed>32767)
				mixed = 32767;
			else if(mixed<-32768)
				mixed = -32768;
			dstBuffer[dstOffset] = mixed;
		}
	}

	return dstOffset<<1;
}

PVM_FORCEINLINE int MixDataToStereo8(signed char* srcBuffer, int srcChanels, int srcSamples, signed char* dstBuffer, int mixingVolume, int panning)
{
	int dstOffset = 0;
	int mixedL, mixedR;

	if(panning==0)
	{
		if(mixingVolume==256)
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix8stereo(&srcBuffer, srcChanels, mixedL, mixedR);
				mixedL += dstBuffer[dstOffset];
				mixedR += dstBuffer[dstOffset + 1];

				if(mixedL>127)
					mixedL = 127;
				else if(mixedL<-128)
					mixedL = -128;

				if(mixedR>127)
					mixedR = 127;
				else if(mixedR<-128)
					mixedR = -128;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
		else
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix8stereo(&srcBuffer, srcChanels, mixedL, mixedR);
				mixedL = dstBuffer[dstOffset] + (mixedL*mixingVolume>>8);
				mixedR = dstBuffer[dstOffset + 1] + (mixedR*mixingVolume>>8);

				if(mixedL>127)
					mixedL = 127;
				else if(mixedL<-128)
					mixedL = -128;

				if(mixedR>127)
					mixedR = 127;
				else if(mixedR<-128)
					mixedR = -128;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
	}
	else
	{
		/////////////////////
		//with panning
		int newSampleL;
		int newSampleR;

		if(mixingVolume==256)
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix8stereo(&srcBuffer, srcChanels, mixedL, mixedR);

				if(panning<0)
				{
					newSampleL = mixedL + ((mixedR*(-panning))>>8);
					newSampleR = (mixedR*(256 + panning))>>8;
				}
				else
				{
					newSampleR = mixedR + ((mixedL*(panning))>>8);
					newSampleL = (mixedL*(256 - panning))>>8;
				}

				newSampleL += dstBuffer[dstOffset];
				newSampleR += dstBuffer[dstOffset + 1];

				if(newSampleL>127)
					newSampleL = 127;
				else if(newSampleL<-128)
					newSampleL = -128;

				if(newSampleR>127)
					newSampleR = 127;
				else if(newSampleR<-128)
					newSampleR = -128;

				dstBuffer[dstOffset] = newSampleL;
				dstBuffer[dstOffset + 1] = newSampleR;
			}
		}
		else
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix8stereo(&srcBuffer, srcChanels, mixedL, mixedR);

				if(panning<0)
				{
					newSampleL = mixedL + ((mixedR*(-panning))>>8);
					newSampleR = (mixedR*(256 + panning))>>8;
				}
				else
				{
					newSampleR = mixedR + ((mixedL*(panning))>>8);
					newSampleL = (mixedL*(256 - panning))>>8;
				}

				newSampleL = dstBuffer[dstOffset] + (newSampleL*mixingVolume>>8);
				newSampleR = dstBuffer[dstOffset + 1] + (newSampleR*mixingVolume>>8);

				if(newSampleL>127)
					newSampleL = 127;
				else if(newSampleL<-128)
					newSampleL = -128;

				if(newSampleR>127)
					newSampleR = 127;
				else if(newSampleR<-128)
					newSampleR = -128;

				dstBuffer[dstOffset] = newSampleL;
				dstBuffer[dstOffset + 1] = newSampleR;
			}
		}
	}

	return dstOffset;
}

PVM_FORCEINLINE int MixDataToStereo16(short* srcBuffer, int srcChanels, int srcSamples, short* dstBuffer, int mixingVolume, int panning)
{
	int dstOffset = 0;
	int mixedL, mixedR;

	if(panning==0)
	{
		if(mixingVolume==256)
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix16stereo(&srcBuffer, srcChanels, mixedL, mixedR);
				mixedL += dstBuffer[dstOffset];
				mixedR += dstBuffer[dstOffset + 1];

				if(mixedL>32767)
					mixedL = 32767;
				else if(mixedL<-32768)
					mixedL = -32768;

				if(mixedR>32767)
					mixedR = 32767;
				else if(mixedR<-32768)
					mixedR = -32768;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
		else
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix16stereo(&srcBuffer, srcChanels, mixedL, mixedR);
				mixedL = dstBuffer[dstOffset] + (mixedL*mixingVolume>>8) ;
				mixedR = dstBuffer[dstOffset + 1] + (mixedR*mixingVolume>>8) ;

				if(mixedL>32767)
					mixedL = 32767;
				else if(mixedL<-32768)
					mixedL = -32768;

				if(mixedR>32767)
					mixedR = 32767;
				else if(mixedR<-32768)
					mixedR = -32768;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
	}
	else
	{
		///////////////////////
		//with paning

		int newSampleL;
		int newSampleR;

		if(mixingVolume==256)
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix16stereo(&srcBuffer, srcChanels, mixedL, mixedR);
				if(panning<0)
				{
					newSampleL = mixedL + ((mixedR*(-panning))>>8);
					newSampleR = (mixedR*(256 + panning))>>8;
				}
				else
				{
					newSampleR = mixedR + ((mixedL*(panning))>>8);
					newSampleL = (mixedL*(256 - panning))>>8;
				}

				newSampleL += dstBuffer[dstOffset];
				newSampleR += dstBuffer[dstOffset + 1];

				if(newSampleL>32767)
					newSampleL = 32767;
				else if(newSampleL<-32768)
					newSampleL = -32768;

				if(newSampleR>32767)
					newSampleR = 32767;
				else if(newSampleR<-32768)
					newSampleR = -32768;

				dstBuffer[dstOffset] = newSampleL;
				dstBuffer[dstOffset + 1] = newSampleR;
			}
		}
		else
		{
			for(int i=0; i<srcSamples; i+=srcChanels, dstOffset+=2)
			{
				mix16stereo(&srcBuffer, srcChanels, mixedL, mixedR);
				if(panning<0)
				{
					newSampleL = mixedL + ((mixedR*(-panning))>>8);
					newSampleR = (mixedR*(256 + panning))>>8;
				}
				else
				{
					newSampleR = mixedR + ((mixedL*(panning))>>8);
					newSampleL = (mixedL*(256 - panning))>>8;
				}

				newSampleL = dstBuffer[dstOffset] + (newSampleL*mixingVolume>>8);
				newSampleR = dstBuffer[dstOffset + 1] + (newSampleR*mixingVolume>>8);

				if(newSampleL>32767)
					newSampleL = 32767;
				else if(newSampleL<-32768)
					newSampleL = -32768;

				if(newSampleR>32767)
					newSampleR = 32767;
				else if(newSampleR<-32768)
					newSampleR = -32768;

				dstBuffer[dstOffset] = newSampleL;
				dstBuffer[dstOffset + 1] = newSampleR;
			}
		}
	}

	return dstOffset<<1;
}

//***

PVM_FORCEINLINE int MixDataToStereo16FromStereo(short* srcBuffer, int srcSamples, short* dstBuffer, int mixingVolume, int panning)
{
	int dstOffset = 0;
	int mixedL, mixedR;

	if(panning==0)
	{
		if(mixingVolume==256)
		{
			for(int i=0; i<srcSamples; i+=2, dstOffset+=2)
			{
				mixedL = dstBuffer[dstOffset] + *srcBuffer;
				srcBuffer++;
				mixedR = dstBuffer[dstOffset + 1] + *srcBuffer;
				srcBuffer++;

				if(mixedL>32767)
					mixedL = 32767;
				else if(mixedL<-32768)
					mixedL = -32768;

				if(mixedR>32767)
					mixedR = 32767;
				else if(mixedR<-32768)
					mixedR = -32768;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
		else
		{
			for(int i=0; i<srcSamples; i+=2, dstOffset+=2)
			{
				mixedL = dstBuffer[dstOffset] + (*srcBuffer*mixingVolume>>8);
				srcBuffer++;
				mixedR = dstBuffer[dstOffset + 1] + (*srcBuffer*mixingVolume>>8);
				srcBuffer++;

				if(mixedL>32767)
					mixedL = 32767;
				else if(mixedL<-32768)
					mixedL = -32768;

				if(mixedR>32767)
					mixedR = 32767;
				else if(mixedR<-32768)
					mixedR = -32768;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
	}
	else
	{
		///////////////////////
		//with paning

		int newSampleL;
		int newSampleR;
		int panL, panR;

		if(mixingVolume==256)
		{
			if(panning<0)
			{
				panL = -panning;
				panR = (256 + panning);
				for(int i=0; i<srcSamples; i+=2, dstOffset+=2)
				{
					mixedL = *srcBuffer; srcBuffer++;
					mixedR = *srcBuffer; srcBuffer++;

					newSampleL = mixedL + ((*srcBuffer*panL)>>8);
					newSampleR = (mixedR*panR)>>8;

					newSampleL += dstBuffer[dstOffset];
					newSampleR += dstBuffer[dstOffset + 1];

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}
			}
			else
			{
				panL = panning;
				panR = (256 - panning);
				for(int i=0; i<srcSamples; i+=2, dstOffset+=2)
				{
					mixedL = *srcBuffer; srcBuffer++;
					mixedR = *srcBuffer; srcBuffer++;

					newSampleR = mixedR + ((mixedL*panL)>>8);
					newSampleL = (mixedL*panR)>>8;

					newSampleL += dstBuffer[dstOffset];
					newSampleR += dstBuffer[dstOffset + 1];

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}

			}
		}
		else
		{
			if(panning<0)
			{
				panL = -panning;
				panR = (256 + panning);
				for(int i=0; i<srcSamples; i+=2, dstOffset+=2)
				{
					mixedL = *srcBuffer; srcBuffer++;
					mixedR = *srcBuffer; srcBuffer++;

					newSampleL = mixedL + ((mixedR*panL)>>8);
					newSampleR = (mixedR*panR)>>8;

					newSampleL = dstBuffer[dstOffset] + (newSampleL*mixingVolume>>8);
					newSampleR = dstBuffer[dstOffset + 1] + (newSampleR*mixingVolume>>8);

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}

			}
			else
			{
				panL = panning;
				panR = 256 - panning;
				for(int i=0; i<srcSamples; i+=2, dstOffset+=2)
				{
					mixedL = *srcBuffer; srcBuffer++;
					mixedR = *srcBuffer; srcBuffer++;

					newSampleR = mixedR + ((mixedL*panL)>>8);
					newSampleL = (mixedL*panR)>>8;

					newSampleL = dstBuffer[dstOffset] + (newSampleL*mixingVolume>>8);
					newSampleR = dstBuffer[dstOffset + 1] + (newSampleR*mixingVolume>>8);

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}

			}
		}
	}

	return dstOffset<<1;
}

//

PVM_FORCEINLINE int MixDataToStereo16FromMono(short* srcBuffer, int srcSamples, short* dstBuffer, int mixingVolume, int panning)
{
	int dstOffset = 0;
	register int mixedL, mixedR;

	if(panning==0)
	{
		if(mixingVolume==256)
		{
			for(int i=0; i<srcSamples; i++, dstOffset+=2)
			{
				mixedL = dstBuffer[dstOffset] + *srcBuffer;
				mixedR = dstBuffer[dstOffset + 1] + *srcBuffer;
				srcBuffer++;

				if(mixedL>32767)
					mixedL = 32767;
				else if(mixedL<-32768)
					mixedL = -32768;

				if(mixedR>32767)
					mixedR = 32767;
				else if(mixedR<-32768)
					mixedR = -32768;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
		else
		{
			for(int i=0; i<srcSamples; i++, dstOffset+=2)
			{
				mixedL = dstBuffer[dstOffset] + (*srcBuffer*mixingVolume>>8);
				mixedR = dstBuffer[dstOffset + 1] + (*srcBuffer*mixingVolume>>8);
				srcBuffer++;

				if(mixedL>32767)
					mixedL = 32767;
				else if(mixedL<-32768)
					mixedL = -32768;

				if(mixedR>32767)
					mixedR = 32767;
				else if(mixedR<-32768)
					mixedR = -32768;

				dstBuffer[dstOffset] = mixedL;
				dstBuffer[dstOffset + 1] = mixedR;
			}
		}
	}
	else
	{
		///////////////////////
		//with paning

		int newSampleL;
		int newSampleR;

		if(mixingVolume==256)
		{
			int panL, panR;
			if(panning<0)
			{
				panL = -panning;
				panR = 256 + panning;

				for(int i=0; i<srcSamples; i++, dstOffset+=2)
				{
					mixedL = *srcBuffer;
					mixedR = *srcBuffer;
					srcBuffer++;

					newSampleL = mixedL + ((mixedR*panL)>>8);
					newSampleR = (mixedR*panR)>>8;


					newSampleL += dstBuffer[dstOffset];
					newSampleR += dstBuffer[dstOffset + 1];

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}
			}
			else
			{
				panR = panning;
				panL = 256 - panning;

				for(int i=0; i<srcSamples; i++, dstOffset+=2)
				{
					mixedL = *srcBuffer;
					mixedR = *srcBuffer;
					srcBuffer++;

					newSampleR = mixedR + ((mixedL*panR)>>8);
					newSampleL = (mixedL*panL)>>8;

					newSampleL += dstBuffer[dstOffset];
					newSampleR += dstBuffer[dstOffset + 1];

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}
			}
		}
		else
		{
			int panL, panR;
			if(panning<0)
			{
				panL = -panning;
				panR = 256 + panning;
			}
			else
			{
				panR = panning;
				panL = 256 - panning;
			}

			if(panning<0)
			{
				for(int i=0; i<srcSamples; i++, dstOffset+=2)
				{
					mixedL = *srcBuffer;
					mixedR = *srcBuffer;
					srcBuffer++;

					newSampleL = mixedL + ((mixedR*panL)>>8);
					newSampleR = (mixedR*panR)>>8;

					newSampleL = dstBuffer[dstOffset] + (newSampleL*mixingVolume>>8);
					newSampleR = dstBuffer[dstOffset + 1] + (newSampleR*mixingVolume>>8);

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}
			}
			else
			{
				for(int i=0; i<srcSamples; i++, dstOffset+=2)
				{
					mixedL = *srcBuffer;
					mixedR = *srcBuffer;
					srcBuffer++;

					newSampleR = mixedR + ((mixedL*panR)>>8);
					newSampleL = (mixedL*panL)>>8;

					newSampleL = dstBuffer[dstOffset] + (newSampleL*mixingVolume>>8);
					newSampleR = dstBuffer[dstOffset + 1] + (newSampleR*mixingVolume>>8);

					if(newSampleL>32767)
						newSampleL = 32767;
					else if(newSampleL<-32768)
						newSampleL = -32768;

					if(newSampleR>32767)
						newSampleR = 32767;
					else if(newSampleR<-32768)
						newSampleR = -32768;

					dstBuffer[dstOffset] = newSampleL;
					dstBuffer[dstOffset + 1] = newSampleR;
				}
			}
		}
	}

	return dstOffset<<1;
}

/*
PVM_FORCEINLINE int FillBufferMono16(IPVMVoice* voice, short* dstBuffer, int maxSamples)
{
	int nSrcChanels = voice->GetChanelsNum();
	int samplesToFillSrc = maxSamples*nSrcChanels;
	int samplesToFillDst = maxSamples;
	char tmpBuff

	while(samplesToFillSrc>0)
	{

		samplesToFillSrc -= nSrcChanels;
		samplesToFillDst--;
	}

	return maxSamples - samplesToFillDst;
}
*/
#endif
